#ifndef __CONDITION_WIN_H__
#define __CONDITION_WIN_H__

#pragma once
#ifndef _WIN32_WINNT
#define _WIN32_WINNT 0x0401
#endif
#include "DSTHType.h"
//#include <windows.h>
#include <winbase.h> 

class MutexLockGuard {
public:
    explicit MutexLockGuard(HANDLE mutex) : mutex_(mutex) {
        WaitForSingleObject(mutex_, INFINITE);
    }
    ~MutexLockGuard() { ReleaseMutex(mutex_); }
private:
    HANDLE mutex_;
};

typedef struct {
    int waiters_count_;
    // Number of waiting threads.

    CRITICAL_SECTION waiters_count_lock_;
    // Serialize access to <waiters_count_>.

    HANDLE sema_;
    // Semaphore used to queue up threads waiting for the condition to
    // become signaled. 

    HANDLE waiters_done_;
    // An auto-reset event used by the broadcast/signal thread to wait
    // for all the waiting thread(s) to wake up and be released from the
    // semaphore. 

    size_t was_broadcast_;
    // Keeps track of whether we were broadcasting or signaling.  This
    // allows us to optimize the code if we're just signaling.
} pthread_cond_t;

class Condition
{
public:
    Condition(HANDLE mutex) : mutex_(mutex) {
        pcond_.waiters_count_ = 0;
        pcond_.was_broadcast_ = 0;
        pcond_.sema_ = ::CreateSemaphore(NULL,       // no security
            0,          // initially 0
            0x7fffffff, // max count
            NULL);      // unnamed 
        InitializeCriticalSection (&pcond_.waiters_count_lock_);
        pcond_.waiters_done_ = CreateEvent (NULL,  // no security
            FALSE, // auto-reset
            FALSE, // non-signaled initially
            NULL); // unnamed

    }
    ~Condition(void) {
        DeleteCriticalSection(&pcond_.waiters_count_lock_);
        CloseHandle(pcond_.sema_);
        CloseHandle(pcond_.waiters_done_);
    }

    void Wait() {
        // Avoid race conditions.
        EnterCriticalSection (&pcond_.waiters_count_lock_);
        pcond_.waiters_count_++;
        LeaveCriticalSection (&pcond_.waiters_count_lock_);

        // This call atomically releases the mutex and waits on the
        // semaphore until <pthread_cond_signal> or <pthread_cond_broadcast>
        // are called by another thread.
        SignalObjectAndWait (mutex_, pcond_.sema_, INFINITE, FALSE);

        // Reacquire lock to avoid race conditions.
        EnterCriticalSection (&pcond_.waiters_count_lock_);

        // We're no longer waiting...
        pcond_.waiters_count_--;

        // Check to see if we're the last waiter after <pthread_cond_broadcast>.
        int last_waiter = pcond_.was_broadcast_ && pcond_.waiters_count_ == 0;

        LeaveCriticalSection (&pcond_.waiters_count_lock_);

        // If we're the last waiter thread during this particular broadcast
        // then let all the other threads proceed.
        if (last_waiter)
            // This call atomically signals the <waiters_done_> event and waits until
            // it can acquire the <external_mutex>.  This is required to ensure fairness. 
            SignalObjectAndWait (pcond_.waiters_done_, mutex_, INFINITE, FALSE);
        else
            // Always regain the external mutex since that's the guarantee we
            // give to our callers. 
            WaitForSingleObject (mutex_, INFINITE);
    }
    void Notify() {
        EnterCriticalSection (&pcond_.waiters_count_lock_);
        int have_waiters = pcond_.waiters_count_ > 0;
        LeaveCriticalSection (&pcond_.waiters_count_lock_);

        // If there aren't any waiters, then this is a no-op.  
        if (have_waiters)
            ReleaseSemaphore (pcond_.sema_, 1, 0);
    }

    void NotifyAll() {
        // This is needed to ensure that <waiters_count_> and <was_broadcast_> are
        // consistent relative to each other.
        EnterCriticalSection (&pcond_.waiters_count_lock_);
        int have_waiters = 0;

        if (pcond_.waiters_count_ > 0) {
            // We are broadcasting, even if there is just one waiter...
            // Record that we are broadcasting, which helps optimize
            // <pthread_cond_wait> for the non-broadcast case.
            pcond_.was_broadcast_ = 1;
            have_waiters = 1;
        }

        if (have_waiters) {
            // Wake up all the waiters atomically.
            ReleaseSemaphore (pcond_.sema_, pcond_.waiters_count_, 0);

            LeaveCriticalSection (&pcond_.waiters_count_lock_);

            // Wait for all the awakened threads to acquire the counting
            // semaphore. 
            WaitForSingleObject (pcond_.waiters_done_, INFINITE);
            // This assignment is okay, even without the <waiters_count_lock_> held 
            // because no other waiter threads can wake up to access it.
            pcond_.was_broadcast_ = 0;
        }
        else
            LeaveCriticalSection (&pcond_.waiters_count_lock_);
    }
private:
    HANDLE mutex_;
    pthread_cond_t pcond_;
};

#endif // __CONDITION_WIN_H__
